//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/io/scheme/file.h>
#include <buola/io.h>
#include <buola/io/cfile.h>
#include <buola/io/cfdstream.h>
#include <buola/algorithm/split.h>
#include <buola/os/user.h>

#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

namespace buola { namespace io {

bool dir_exists(const char *pPath)
{
    struct stat lStat;

    if(!stat(pPath,&lStat))
    {
        return S_ISDIR(lStat.st_mode);
    }
    return false;
}


CFileScheme::CFileScheme()
    :   CURIScheme("file")
{
}

CFileScheme::~CFileScheme()
{
}

CFileImpl *CFileScheme::GetFileImpl(const CURI &pURI)
{
    return new CFileFileImpl(pURI);
}

void CFileScheme::CreateFolder(const CURI &pURI,bool pParents)
{
    std::string lPath=pURI.Path();

    if(pParents)
    {
        std::vector<std::string> lComponents;
        split(lPath,lComponents,fn::equal_to('/'),nDeleteEmpty);
        if(lComponents.empty())
            throw XIO("can't create folder");
        if(lPath[0]=='/')
            lPath="/";
        else
            lPath.clear();

        bool lNotFound=false;
        for(int i=0;i<lComponents.size();i++)
        {
            if(i!=0) lPath+='/';
            lPath+=lComponents[i];
            if(lNotFound||!dir_exists(lPath.c_str()))
            {
                lNotFound=true;
                ::mkdir(lPath.c_str(),0777);
            }
        }
    }
    else
    {
        ::mkdir(lPath.c_str(),0777);
    }
}

std::string CFileScheme::GetRelativeTo(const CURI& /*pOne*/,const CURI& /*pTo*/)
{
    ///\todo implement this
    return std::string();
}

void CFileScheme::NormalizeURI(CURI& /*pURI*/)
{
    ///\todo implement this
}

void CFileScheme::CanonicalizeURI(CURI& /*pURI*/)
{
    ///\todo implement this
}

bool CFileScheme::Exists(const CURI &pURI)
{
    struct stat lStat;

    if(!stat(pURI.Path().c_str(),&lStat))
    {
        return true;
    }
    return false;
}

bool CFileScheme::IsRegular(const CURI &pURI)
{
    struct stat lStat;

    if(!stat(pURI.Path().c_str(),&lStat))
    {
        return !S_ISDIR(lStat.st_mode);
    }
    return false;
}

bool CFileScheme::IsFolder(const CURI &pURI)
{
    struct stat lStat;

    if(!stat(pURI.Path().c_str(),&lStat))
    {
        return S_ISDIR(lStat.st_mode);
    }
    return false;
}

static CFileScheme sFileScheme;

CURIScheme *file_scheme()
{
    return &sFileScheme;
}

CFileFileImpl::CFileFileImpl(const CURI &pURI)
    :   CFileImpl(pURI)
    ,   mHasInfo(false)
{}

CURI CFileFileImpl::GetTarget()
{
    if(!mHasInfo) RetrieveInfo();
    
    if(!S_ISLNK(mInfo.st_mode))
        return GetURI();
 
    char *lBuffer;
 
    std::string lPath=GetURI().Path();
    
    if(!(lBuffer=realpath(lPath.c_str(),nullptr)))
    {
        //we'll get here for broken links
        lBuffer=alloc_array<char>(PATH_MAX);
        ssize_t lLen;
        if((lLen=readlink(lPath.c_str(),lBuffer,PATH_MAX))==-1)
        {
            free(lBuffer);
            throw XInternal("can't resolve symbolic link");
        }
 
        lBuffer[lLen]=0;
        if(lBuffer[0]!='/')
        {
            std::string lRelative(lBuffer);
            free(lBuffer);
            return io::file(GetURI().Folder()+'/'+lRelative);
        }
    }
    
    std::string lReturn(lBuffer);
    free(lBuffer);
 
    return io::file(lReturn);
}

CCalendarTime CFileFileImpl::GetAccessTime()
{
    return CCalendarClock::from_time_t(mInfo.st_atime);
}

CCalendarTime CFileFileImpl::GetCreationTime()
{
#if BUOLA_PLATFORM_MAC
    return CCalendarClock::from_time_t(mInfo.st_birthtime);
#else //BUOLA_PLATFORM_MAC
    return CCalendarClock::from_time_t(mInfo.st_ctime);
#endif //BUOLA_PLATFORM_MAC
}

CCalendarTime CFileFileImpl::GetModificationTime()
{
    return CCalendarClock::from_time_t(mInfo.st_mtime);
}

EFileType CFileFileImpl::GetType()
{
    if(!mHasInfo) RetrieveInfo();
    
    EFileType lType=EFileType::NONE;
    
    if(S_ISLNK(mInfo.st_mode))
        lType|=EFileType::LINK;
    if(S_ISREG(mInfo.st_mode))
        lType|=EFileType::REGULAR;
    if(S_ISDIR(mInfo.st_mode))
        lType|=EFileType::FOLDER;
    if(S_ISCHR(mInfo.st_mode))
        lType|=EFileType::CHAR;
    if(S_ISBLK(mInfo.st_mode))
        lType|=EFileType::BLOCK;
    if(S_ISFIFO(mInfo.st_mode))
        lType|=EFileType::FIFO;
    if(S_ISSOCK(mInfo.st_mode))
        lType|=EFileType::SOCKET;

    return lType;
}

ssize_t CFileFileImpl::GetSize()
{
    if(!mHasInfo) RetrieveInfo();

    return mInfo.st_size;
}

os::CUser CFileFileImpl::GetOwner()
{
    if(!mHasInfo) RetrieveInfo();

    return os::CUser(mInfo.st_uid);
}

os::CGroup CFileFileImpl::GetGroup()
{
    if(!mHasInfo) RetrieveInfo();

    return os::CGroup(mInfo.st_gid);
}

EPermission CFileFileImpl::GetPermission()
{
    if(!mHasInfo) RetrieveInfo();

    return io::EPermission(mInfo.st_mode)&io::EPermission::ALL;
}

void CFileFileImpl::RetrieveInfo()
{
    if(lstat(GetURI().Path().c_str(),&mInfo))
    {
        switch(errno)
        {
        case EACCES:
        case EPERM:
        case EROFS:
            throw XPermission(GetURI().ToString()+
                    I18N(": Not enough permissions for stating file"));
        case ENAMETOOLONG:
        case ENOTDIR:
        case ENOENT:
        case ELOOP:
            throw XNotFound(GetURI().ToString()+
                    I18N(": Can't stat file... file not found"));
        default:
            throw XInternal(GetURI().ToString()+
                    I18N(": Unexpected error while stating file"));
        }
    }

    mHasInfo=true;
}

bool CFileFileImpl::CanRead()
{
    if(!mHasInfo) RetrieveInfo();

    os::CUser lUser=os::get_user();

    if(lUser.IsSuperUser()) return true; //root can read everything

    if(lUser.ID()==mInfo.st_uid) //we are owner
        return (mInfo.st_mode&S_IRUSR);

    if(get_group_membership(os::CGroup(mInfo.st_gid))) //we are group
        return (mInfo.st_mode&S_IRGRP);

    return (mInfo.st_mode&S_IROTH);
}

bool CFileFileImpl::CanWrite()
{
    if(!mHasInfo) RetrieveInfo();

    os::CUser lUser=os::get_user();

    if(lUser.IsSuperUser()) return true; //root can write everything

    if(lUser.ID()==mInfo.st_uid) //we are owner
        return (mInfo.st_mode&S_IWUSR);

    if(get_group_membership(os::CGroup(mInfo.st_gid))) //we are group
        return (mInfo.st_mode&S_IWGRP);

    return (mInfo.st_mode&S_IWOTH);
}

bool CFileFileImpl::CanExecute()
{
    if(!mHasInfo) RetrieveInfo();

    os::CUser lUser=os::get_user();

    if(lUser.IsSuperUser()) return (mInfo.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH)); //root can execute if someone can

    if(lUser.ID()==mInfo.st_uid) //we are owner
        return (mInfo.st_mode&S_IXUSR);

    if(get_group_membership(os::CGroup(mInfo.st_gid))) //we are group
        return (mInfo.st_mode&S_IXGRP);

    return (mInfo.st_mode&S_IXOTH);
}

bool CFileFileImpl::CanChangeOwner()
{
    return os::get_user().IsSuperUser();
}

bool CFileFileImpl::CanChangeGroup()
{
    if(!mHasInfo) RetrieveInfo();

    os::CUser lUser=os::get_user();

    return (lUser.IsSuperUser()||lUser.ID()==mInfo.st_uid);
}

bool CFileFileImpl::CanChangePermissions()
{
    if(!mHasInfo) RetrieveInfo();

    os::CUser lUser=os::get_user();

    return (lUser.IsSuperUser()||lUser.ID()==mInfo.st_uid);
}

std::vector<CFile> CFileFileImpl::GetChildren(PFileFilter pFilter)
{
    ///\todo implement parameters
    std::vector<CFile> lReturn;
    
    DIR *lDIR=opendir(GetURI().Path().c_str());
    if(!lDIR)
    {
        switch(errno)
        {
            case EACCES:
                throw XPermission(GetURI().Path()+I18N(": Not enough permissions to read directory"));
            case EMFILE:
            case ENFILE:
                throw XFull(GetURI().Path()+I18N(": Too many files"));
            case ENOENT:
            case ENOTDIR:
                throw XNotFound(GetURI().Path()+I18N(": File not found or is not a directory"));
            default:
                throw XInternal(GetURI().Path()+I18N(": Unknown error opening directory"));
        }
    }
    
    dirent lData;
    dirent *lResult;
    
    while(!readdir_r(lDIR,&lData,&lResult))
    {
        if(!lResult) break;
        
        std::string lName=lResult->d_name;
        if(lName.length()<=2)
        {
            if(lName.length()==0)
                continue;
            if(lName[0]=='.')
            {
                if(lName.length()==1)
                    continue;
                if(lName[1]=='.')
                    continue;
            }
        }

        CFile lFile(GetURI()/lName);
        
        if(pFilter&&!pFilter->CheckFile(lFile))
            continue;
        
        lReturn.push_back(std::move(lFile));
    }
    
    closedir(lDIR);
    
    return lReturn;
}
    
PStream CFileFileImpl::GetStream(io::EMode pMode,io::EPermission pPerm)
{
    return open_disk_file(GetURI().Path(),pMode,pPerm);
}

/*namespace io*/ } /*namespace buola*/ }

// #if BUOLA_PLATFORM_LINUX
// #include "cinotifymonitor.h"
// #include <mntent.h>
// #endif //BUOLA_PLATFORM_LINUX
// 
// namespace buola {
// 
// CDir::~CDir()
// {
//     if(mIsRead) UnRead();
// #if BUOLA_PLATFORM_LINUX
//     if(mMonitor)
//         delete mMonitor;
// #endif //BUOLA_PLATFORM_LINUX
// }
// 
// void CDir::Read()
// {
//     if(mIsRead) UnRead();
// 
//     DIR *lDIR=opendir(mFullPath.c_str());
//     if(!lDIR)
//     {
//         switch(errno)
//         {
//         case EACCES:
//             throw XPermission(mFullPath+I18N(": Not enough permissions to read directory"));
//         case EMFILE:
//         case ENFILE:
//             throw XFull(mFullPath+I18N(": Too many files"));
//         case ENOENT:
//         case ENOTDIR:
//             throw XNotFound(mFullPath+I18N(": File not found or is not a directory"));
//         default:
//             throw XInternal(mFullPath+I18N(": Unknown error opening directory"));
//         }
//     }
// 
//     mIsRead=true;
// 
//     dirent lData;
//     dirent *lResult;
// 
//     while(!readdir_r(lDIR,&lData,&lResult))
//     {
//         if(!lResult) break;
// 
//         //TODO:decide some proper encoding
//         std::string lName=lResult->d_name;
//         CURL lURL=mURL+uri_escape(lName);
// 
//         AddEntry(lName,lURL);
//     }
// 
//     closedir(lDIR);
// }
// 
// bool CDir::HasAnyFolderChild(const PFileFilter &pFilter)
// {
//     if(mIsRead)
//         return CFolder::HasAnyFolderChild();
// 
//     DIR *lDIR=opendir(mFullPath.c_str());
//     if(!lDIR)
//     {
//         switch(errno)
//         {
//         case EACCES:
//             throw XPermission(mFullPath+I18N(": Not enough permissions to read directory"));
//         case EMFILE:
//         case ENFILE:
//             throw XFull(mFullPath+I18N(": Too many files"));
//         case ENOENT:
//         case ENOTDIR:
//             throw XNotFound(mFullPath+I18N(": File not found or is not a directory"));
//         default:
//             throw XInternal(mFullPath+I18N(": Unknown error opening directory"));
//         }
//     }
// 
//     dirent lData;
//     dirent *lResult;
// 
//     bool lFound=false;
// 
//     while(!readdir_r(lDIR,&lData,&lResult))
//     {
//         if(!lResult) break;
// 
//         std::string lName(lData.d_name);
// 
//         if(!pFilter->CheckName(lName)) continue;
// 
//         if(lData.d_type==DT_DIR)
//         {
//             lFound=true;
//             break;
//         }
//         else if(lData.d_type==DT_UNKNOWN //it happens with reiser
//                     ||lData.d_type==DT_LNK)
//         {
//             //TODO:decide some proper encoding
//             CURL lURL=mURL+lName;
//             struct stat lStat;
//             stat(uri_unescape(lURL.GetFullPath()).c_str(),&lStat);
//             if(S_ISDIR(lStat.st_mode))
//             {
//                 lFound=true;
//                 break;
//             }
//         }
//     }
// 
//     closedir(lDIR);
// 
//     return lFound;
// }
// 
// #if BUOLA_PLATFORM_LINUX
// void CDir::Mount()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     if(!mMountInfo) throw XIO(I18N("Can't mount directory"));
// 
//     mount(mMountInfo->mDevice.c_str(),mFullPath.c_str(),
//                 mMountInfo->mType.c_str(),MS_RDONLY,"");
// 
//     mMountInfo->mMounted=true;
// }
// 
// void CDir::UnMount()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     if(!mMountInfo) throw XIO(I18N("Can't unmount directory"));
// 
//     umount2(mFullPath.c_str(),0);
// 
//     mMountInfo->mMounted=false;
// }
// 
// void CDir::ReadMountInfo()
// {
//     if(mHasMountInfo) return;
// 
//     setfsent();
//     fstab *lEntry=getfsfile(mFullPath.c_str());
// 
//     if(!lEntry)
//     {
//         mMountInfo=nullptr;
//     }
//     else
//     {
//         mMountInfo=new SMountInfo;
//         mMountInfo->mDevice=lEntry->fs_spec;
//         mMountInfo->mType=lEntry->fs_vfstype;
//         mMountInfo->mOptions=lEntry->fs_mntops;
//         mMountInfo->mMode=lEntry->fs_type;
// 
//         mMountInfo->mMounted=false;
// 
//         FILE *lMtab=setmntent("/etc/mtab","r");
//         mntent *lMEntry;
//         while((lMEntry=getmntent(lMtab)))
//         {
//             if(!strcmp(lEntry->fs_file,lMEntry->mnt_dir))
//             {
//                 mMountInfo->mMounted=true;
//                 break;
//             }
//         }
//     }
// 
//     mHasMountInfo=true;
// 
//     return;
// }
// #else //BUOLA_PLATFORM_LINUX
// void CDir::Mount()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     if(!mMountInfo) throw XIO(I18N("Can't mount directory"));
// 
//     mount(mMountInfo->mType.c_str(),mFullPath.c_str(),MNT_RDONLY,
//                 const_cast<char*>(mMountInfo->mDevice.c_str()));
// 
//     mMountInfo->mMounted=true;
// }
// 
// void CDir::UnMount()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     if(!mMountInfo) throw XIO(I18N("Can't unmount directory"));
// 
//     unmount(mFullPath.c_str(),0);
// 
//     mMountInfo->mMounted=false;
// }
// 
// void CDir::ReadMountInfo()
// {
//     if(mHasMountInfo) return;
// 
//     struct statfs lBuf[128];
//     
//     int lCount=getfsstat(lBuf,128,MNT_NOWAIT);
//     
//     mMountInfo=nullptr;
//     
//     for(int i=0;i<lCount;i++)
//     {
//         if(!strcmp(mFullPath.c_str(),lBuf[i].f_mntonname))
//         {
//             mMountInfo=new SMountInfo;
//             mMountInfo->mDevice=lBuf[i].f_mntfromname;
//             mMountInfo->mType=lBuf[i].f_fstypename;
//             mMountInfo->mMounted=true;
//         }
//     }
// 
//     mHasMountInfo=true;
// 
//     return;
// }
// #endif //BUOLA_PLATFORM_LINUX
// 
// bool CDir::IsMountPoint()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     return (mMountInfo!=nullptr);
// }
// 
// bool CDir::IsMounted()
// {
//     if(!mHasMountInfo) ReadMountInfo();
// 
//     return mMountInfo->mMounted;
// }
// 
// void CDir::Rename(const std::string &pNew)
// {
//     mHasInfo=false;
// 
//     CURL lNewURL(mURL);
// 
//     lNewURL.MakeParent();
//     lNewURL.AppendOrReplacePath(pNew);
// 
//     std::string lNewFullPath=uri_unescape(lNewURL.GetFullPath());
//     
//     if(!rename(mFullPath.c_str(),lNewFullPath.c_str()))
//     {
//         mURL=lNewURL;
//         mFullPath=lNewFullPath;
//         return;
//     }
// 
//     switch(errno)
//     {
//     case EACCES:
//     case EPERM:
//     case EROFS:
//     case EXDEV:
//         throw XPermission(I18N("Don't have permission to rename file"));
//     case ENAMETOOLONG:
//         throw XArgument(I18N("Wrong name for directory"));
//     case ENOTDIR:
//     case ENOENT:
//     case ELOOP:
//         throw XNotFound(mFullPath+I18N(": File not found or is not a directory"));
//     case EEXIST:
//         throw XAlready(I18N("File already exists while renaming"));
//     default:
//         throw XInternal(I18N("Unknown error renaming directory"));
//     }
// }
// 
// void CDir::Move(const CURL &pDest)
// {
//     CDir lDestFile(pDest);
// 
//     if(lDestFile.Exists())
//     {
//         if(lDestFile.IsFolder())
//         {
//             return Move(pDest+mURL.GetName());
//         }
// 
//         throw XAlready(I18N("Dir already exists while moving"));
//     }
// 
//     if(!rename(mFullPath.c_str(),uri_unescape(pDest.GetFullPath()).c_str()))
//     {
//         return;
//     }
// 
//     throw XInternal(I18N("Unexpected error while moving directory"));
// }
// 
// 
// void CDir::StartMonitor()
// {
// ///\todo port this to other platforms
// #if BUOLA_PLATFORM_LINUX
//     if(!mMonitor) mMonitor=new CINotifyMonitor(this);
//     mMonitor->Start();
// #endif //BUOLA_PLATFORM_LINUX
// }
// 
// void CDir::StopMonitor()
// {
// #if BUOLA_PLATFORM_LINUX
//     if(mMonitor)
//     {
//         mMonitor->Cancel();
//         delete mMonitor;
//         mMonitor=nullptr;
//     }
// #endif //BUOLA_PLATFORM_LINUX
// }
// 
// void CDir::FAMChanged(char *pName)
// {
//     std::string lName(pName);
//     CURL lURL=mURL+lName;
//     eChanged(mURL+lName);
// }
// 
// void CDir::FAMDeleted(char *pName)
// {
//     lock_guard<mutex> lLock(mMutex);
//     std::string lName(pName);
//     RemoveEntry(lName);
//     eDeleted(mURL+lName);
// }
// 
// void CDir::FAMCreated(char *pName)
// {
//     lock_guard<mutex> lLock(mMutex);
//     std::string lName(pName);
//     CURL lURL=mURL+lName;
//     AddEntry(lName,lURL);
//     eCreated(lURL);
// }
// 
// /*namespace buola*/ }
/*////////////////////////////////////////////////////////////////////////////////
 //
 This file is part of the buola project (https://code.google.com/p/buola/).  //
 //
 Copyright(c) 2007-2012 Xavi Gratal                                          //
 gratal AT gmail DOT com                                                     //
 //
 Buola is free software: you can redistribute it and/or modify               //
 it under the terms of the GNU General Public License as published by        //
 the Free Software Foundation, either version 3 of the License, or           //
 (at your option) any later version.                                         //
 //
 Buola is distributed in the hope that it will be useful,                    //
 but WITHOUT ANY WARRANTY; without even the implied warranty of              //
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
 GNU General Public License for more details.                                //
 //
 You should have received a copy of the GNU General Public License           //
 along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
 //
 ////////////////////////////////////////////////////////////////////////////////
 
 #include <buola/buola.h>
 
 #include <buola/io/cdiskfile.h>
 #include <buola/io/cfolder.h>
 #include <buola/io/device_disk.h>
 
 #include <errno.h>
 #include <sys/stat.h>
 
 #define COPY_BUF_SIZE 262144
 
 namespace buola {
 
 ///////////////////// File system interface ////////////////////////
 
 
 bool CDiskFile::Exists()
 {
 if(mHasInfo) return true;
 try
 {
 GetInfo();
 }
 catch(XNotFound&)
 {
 return false;
 }
 catch(XPermission&)
 {
 return false;
 }
 return mHasInfo;
 }
 
 void CDiskFile::Remove()
 {
 mHasInfo=false;
 
 if(!remove(mFullPath.c_str()))
 return;
 
 switch(errno)
 {
 case EACCES:
 case EPERM:
 case EROFS:
 throw XPermission(I18N("Not enough permissions for deleting file"));
 case ENAMETOOLONG:
 case ENOTDIR:
 case ENOENT:
 case ELOOP:
 throw XNotFound(I18N("Can't delete file... file not found"));
 default:
 throw XInternal(I18N("Unexpected error while deleting file"));
 }
 }
 
 void CDiskFile::Rename(const std::string &pNew)
 {
 mHasInfo=false;
 
 CURL lNewURL(mURL);
 
 lNewURL.MakeParent();
 lNewURL.AppendOrReplacePath(pNew);
 
 std::string lNewFullPath=uri_unescape(lNewURL.GetFullPath());
 
 if(!rename(mFullPath.c_str(),lNewFullPath.c_str()))
 {
 mURL=lNewURL;
 mFullPath=lNewFullPath;
 return;
 }
 
 switch(errno)
 {
 case EACCES:
 case EPERM:
 case EROFS:
 case EXDEV:
 throw XPermission(I18N("Not enough permissions for renaming file"));
 case ENAMETOOLONG:
 case ENOTDIR:
 case ENOENT:
 case ELOOP:
 throw XNotFound(I18N("Can't rename file... file not found"));
 case EEXIST:
 throw XAlready(I18N("Can't rename file... file already exists"));
 default:
 throw XInternal(I18N("Unexpected error renaming file"));
 }
 }
 
 void CDiskFile::Copy(const CURL &pDest,bool pOverwrite)
 {
 TFileSize lSize=GetSize();
 
 CDiskFile lDestFile(pDest);
 
 if(lDestFile.Exists())
 {
 if(lDestFile.IsFolder())
 {
 return Copy(pDest+mURL.GetName());
 }
 
 if(!pOverwrite)
 throw XAlready(I18N("Can't copy file... file already exists"));
 }
 
 io::PStream lDestStream=lDestFile.GetStream(io::EMode::OPENORCREATE);
 io::PStream lSrcStream=GetStream(io::EMode::OPEN);
 
 char lBuffer[COPY_BUF_SIZE];
 
 int lResult;
 
 try
 {
 int lPercent=0;
 TFileSize lWritten=0;
 eIOProgress(lPercent);
 while((lResult=lSrcStream->read(lBuffer,COPY_BUF_SIZE).gcount())>0)
 {
 lDestStream->write(lBuffer,lResult);
 lWritten+=lResult;
 
 if(lWritten*100/lSize>lPercent)
 {
 lPercent=lWritten*100/lSize;
 eIOProgress(lPercent);
 msg_info() << lPercent << "%\n";
 }
 }
 if(lPercent<100)
 {
 eIOProgress(100);
 }
 }
 catch(XPermission &pE)
 {
 msg_error() << "Permission problem " << pE.what() << "\n";
 }
 catch(XHardware &pE)
 {
 msg_error() << "Hardware problem " << pE.what() << "\n";
 }
 
 delete lDestStream;
 delete lSrcStream;
 }
 
 void CDiskFile::Move(const CURL &pDest)
 {
 mHasInfo=false;
 
 std::string lDest=uri_unescape(pDest.GetFullPath());
 
 CDiskFile lDestFile(pDest);
 
 if(lDestFile.Exists())
 {
 if(lDestFile.IsFolder())
 {
 return Move(pDest+"/"+mURL.GetName());
 }
 
 throw XAlready(I18N("File already exists while moving"));
 }
 
 if(!rename(mFullPath.c_str(),lDest.c_str()))
 {
 return;
 }
 
 throw XInternal(I18N("Unexpected error while moving file"));
 }
 
 void CDiskFile::Touch()
 {
 throw XInternal(I18N("Not implemented function CDiskFile::Touch"));
 }
 
 void CDiskFile::GetInfo()
 {
 lock_guard<mutex> lLock(mMutex);
 if(mHasInfo) return;
 mHasInfo=false;
 
 struct stat lStat;
 
 if(!lstat(mFullPath.c_str(),&lStat))
 {
 mInfo=TYPE_NONE;
 
 mMTime=CCalendarClock::from_time_t(lStat.st_mtime);
 mCTime=CCalendarClock::from_time_t(lStat.st_ctime);
 mATime=CCalendarClock::from_time_t(lStat.st_atime);
 
 mSize=lStat.st_size;
 
 if(S_ISLNK(lStat.st_mode))
 mInfo|=TYPE_LINK;
 if(S_ISREG(lStat.st_mode))
 mInfo|=TYPE_REGULAR;
 if(S_ISDIR(lStat.st_mode))
 mInfo|=TYPE_FOLDER;
 if(S_ISCHR(lStat.st_mode))
 mInfo|=TYPE_CHAR;
 if(S_ISBLK(lStat.st_mode))
 mInfo|=TYPE_BLOCK;
 if(S_ISFIFO(lStat.st_mode))
 mInfo|=TYPE_FIFO;
 if(S_ISSOCK(lStat.st_mode))
 mInfo|=TYPE_SOCKET;
 
 mPerm=io::EPermission(lStat.st_mode&(int)io::PERM_ALL);
 
 mUID=lStat.st_uid;
 mGID=lStat.st_gid;
 
 mHasInfo=true;
 
 return;
 }
 
 switch(errno)
 {
 case EACCES:
 case EPERM:
 case EROFS:
 throw XPermission(mFullPath+
 I18N(": Not enough permissions for stating file"));
 case ENAMETOOLONG:
 case ENOTDIR:
 case ENOENT:
 case ELOOP:
 throw XNotFound(mFullPath+
 I18N(": Can't stat file... file not found"));
 default:
 perror("strange error:");
 throw XInternal(mFullPath+
 I18N(": Unexpected error while stating file"));
 }
 }
 
 
 PFolder CDiskFile::GetFolder()
 {
 if(!mHasInfo)
 GetInfo();
 if(!mHasInfo)
 return nullptr;
 if(IsFolder())
 return CFolder::Get(mURL);
 
 return nullptr;
 }
 */
